#pragma once

using namespace System;
using namespace System::IO;
using namespace NUnit::Framework;
using namespace LatoolNet;

namespace LatoolNetTest {

	[TestFixture]
	public ref class MatrixTest {
	public:

		[Test]
		void TestSaveLoad() {

			int rownum = 6;
			int colnum = 6;
			Matrix ^ origmat = gcnew Matrix(rownum, colnum);

			Random^ randgen = gcnew Random();

			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					origmat[i, j] = randgen->NextDouble() * 10;
				}
			}

			MatrixFormatter^ format = gcnew MatrixFormatter();
			format->NumberOfDigit = 0;
			Matrix::Save(origmat, format, "./savedat.txt");

			Matrix ^ loadmat = Matrix::Load("./savedat.txt");

			Assert::AreEqual(origmat->RowNum, loadmat->RowNum, "Test: Save/Load");
			Assert::AreEqual(origmat->ColNum, loadmat->ColNum, "Test: Save/Load");

			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					Assert::AreEqual(origmat[i, j], loadmat[i, j], 1e-10, "Test: Save/Load");
				}
			}

			//File::Delete("./savedat.txt");
		};

		[Test]
		[ExpectedException(LatoolException::typeid)]
		void TestOutOfUnderRangeIndex() {

			Matrix ^ mat = gcnew Matrix(3, 3);
			mat[-1,0] = 1.123;

		};

		[Test]
		[ExpectedException(LatoolException::typeid)]
		void TestOutOfOverRangeIndex() {

			Matrix ^ mat = gcnew Matrix(3, 3);
			mat[3,0] = 1.123;

		};

		[Test]
		void TestRowVector() {

			Matrix^ expected = gcnew Matrix(4, 4);
			Random^ rgen = gcnew Random();

			expected[0, 0] = rgen->Next(-10, 10);
			expected[0, 1] = rgen->Next(-10, 10);
			expected[0, 2] = rgen->Next(-10, 10);
			expected[0, 3] = rgen->Next(-10, 10);

			expected[1, 0] = rgen->Next(-10, 10);
			expected[1, 1] = rgen->Next(-10, 10);
			expected[1, 2] = rgen->Next(-10, 10);
			expected[1, 3] = rgen->Next(-10, 10);

			expected[2, 0] = rgen->Next(-10, 10);
			expected[2, 1] = rgen->Next(-10, 10);
			expected[2, 2] = rgen->Next(-10, 10);
			expected[2, 3] = rgen->Next(-10, 10);

			expected[3, 0] = rgen->Next(-10, 10);
			expected[3, 1] = rgen->Next(-10, 10);
			expected[3, 2] = rgen->Next(-10, 10);
			expected[3, 3] = rgen->Next(-10, 10);

			Matrix^ actual = gcnew Matrix(4, 4);
			for (int r = 0; r < 4; r++) {
				Matrix^ rowVector = Matrix::RowVector(expected, r);
				for (int c = 0; c < 4; c++) {
					actual[r, c] = rowVector[0, c];
				}
			}

			Matrix^ verify = expected->Inv() * actual;

			for (int i = 0; i < 4; i++) {
				for (int j = 0; j < 4; j++) {
					if (i == j) {
						Assert::AreEqual(1.0, verify[i, j], 1e-10, "Test: RowVector value");
					} else {
						Assert::AreEqual(0.0, verify[i, j], 1e-10, "Test: RowVector value");
					}
				}
			}

		};

		[Test]
		void TestColVector() {

			Matrix^ expected = gcnew Matrix(4, 4);
			Random^ rgen = gcnew Random();

			expected[0, 0] = rgen->Next(-10, 10);
			expected[0, 1] = rgen->Next(-10, 10);
			expected[0, 2] = rgen->Next(-10, 10);
			expected[0, 3] = rgen->Next(-10, 10);

			expected[1, 0] = rgen->Next(-10, 10);
			expected[1, 1] = rgen->Next(-10, 10);
			expected[1, 2] = rgen->Next(-10, 10);
			expected[1, 3] = rgen->Next(-10, 10);

			expected[2, 0] = rgen->Next(-10, 10);
			expected[2, 1] = rgen->Next(-10, 10);
			expected[2, 2] = rgen->Next(-10, 10);
			expected[2, 3] = rgen->Next(-10, 10);

			expected[3, 0] = rgen->Next(-10, 10);
			expected[3, 1] = rgen->Next(-10, 10);
			expected[3, 2] = rgen->Next(-10, 10);
			expected[3, 3] = rgen->Next(-10, 10);

			Matrix^ actual = gcnew Matrix(4, 4);
			for (int c = 0; c < 4; c++) {
				Matrix^ colVector = Matrix::ColVector(expected, c);
				for (int r = 0; r < 4; r++) {
					actual[r, c] = colVector[r, 0];
				}
			}

			Matrix^ verify = expected->Inv() * actual;

			for (int i = 0; i < 4; i++) {
				for (int j = 0; j < 4; j++) {
					if (i == j) {
						Assert::AreEqual(1.0, verify[i, j], 1e-10, "Test: ColVector value");
					} else {
						Assert::AreEqual(0.0, verify[i, j], 1e-10, "Test: ColVector value");
					}
				}
			}
		};

	};

}